use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::Path;
use tokio::fs;
use wanmesh_common::net::srv6;

pub type Arphrd = u32;
pub const ARPHRD_ETHER: Arphrd = 1;
pub const ARPHRD_NONE: Arphrd = 0xFFFE;

pub const LOCAL_ROUTES_MAX_LENGTH: usize = 1024;

pub const SRV6_SEGMENT_LIST_MAX_LENGTH: usize = srv6::SEGMENT_LIST_MAX_LENGTH;
pub const SRV6_SID_CONFIGS_MAX_LENGTH: usize = 1024;

pub type DeviceType = u32;
pub const DEVICE_TYPE_ETHERNET: DeviceType = 1;
pub const DEVICE_TYPE_RAW_IP: DeviceType = 2;

pub type Srv6Behavior = u16;
pub const SRV6_BEHAVIOR_END: Srv6Behavior = srv6::Behavior::End as u16;
pub const SRV6_BEHAVIOR_END_DT46: Srv6Behavior = srv6::Behavior::EndDt46 as u16;

pub type Srv6Segments = [Ipv6Addr8; SRV6_SEGMENT_LIST_MAX_LENGTH];

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug, Default)]
pub struct Ipv4Addr8 {
    pub addr8: [u8; 4],
}

impl From<&Ipv4Addr> for Ipv4Addr8 {
    fn from(value: &Ipv4Addr) -> Self {
        Self {
            addr8: value.octets(),
        }
    }
}

impl From<Ipv4Addr> for Ipv4Addr8 {
    fn from(value: Ipv4Addr) -> Self {
        Self::from(&value)
    }
}

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug, Default)]
pub struct Ipv6Addr8 {
    pub addr8: [u8; 16],
}

impl From<&Ipv6Addr> for Ipv6Addr8 {
    fn from(value: &Ipv6Addr) -> Self {
        Self {
            addr8: value.octets(),
        }
    }
}

impl From<Ipv6Addr> for Ipv6Addr8 {
    fn from(value: Ipv6Addr) -> Self {
        Self::from(&value)
    }
}

impl From<&Option<Ipv6Addr>> for Ipv6Addr8 {
    fn from(value: &Option<Ipv6Addr>) -> Self {
        match value {
            Some(value) => Self::from(value),
            None => Self::default(),
        }
    }
}

impl From<Option<Ipv6Addr>> for Ipv6Addr8 {
    fn from(value: Option<Ipv6Addr>) -> Self {
        Self::from(&value)
    }
}

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]
pub struct Srv6Route {
    pub segments: Srv6Segments,
}

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]
pub struct Ipv4LpmKey {
    pub prefix_length: u32,
    pub address: Ipv4Addr8,
}

impl From<&cidr::Ipv4Inet> for Ipv4LpmKey {
    fn from(value: &cidr::Ipv4Inet) -> Self {
        Self {
            prefix_length: value.network_length() as u32,
            address: value.address().into(),
        }
    }
}

impl From<cidr::Ipv4Inet> for Ipv4LpmKey {
    fn from(value: cidr::Ipv4Inet) -> Self {
        Self::from(&value)
    }
}

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]
pub struct Ipv6LpmKey {
    pub prefix_length: u32,
    pub address: Ipv6Addr8,
}

impl From<&cidr::Ipv6Inet> for Ipv6LpmKey {
    fn from(value: &cidr::Ipv6Inet) -> Self {
        Self {
            prefix_length: value.network_length() as u32,
            address: value.address().into(),
        }
    }
}

impl From<cidr::Ipv6Inet> for Ipv6LpmKey {
    fn from(value: cidr::Ipv6Inet) -> Self {
        Self::from(&value)
    }
}

#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]
pub struct LocalSrv6SidConfig {
    pub behavior: Srv6Behavior,
}

pub async fn get_iface_type(name: &str) -> Result<Arphrd, anyhow::Error> {
    let path = Path::new("/sys/class/net").join(name).join("type");
    let content = fs::read_to_string(path).await.map_err(anyhow::Error::new)?;
    let dev_type: Arphrd = content.trim().parse().map_err(anyhow::Error::new)?;
    Ok(dev_type)
}
